package com.linln.admin.app.service.task;

import com.linln.admin.app.constants.AppConstants;
import com.linln.admin.system.domain.TaskQueue;
import com.linln.admin.system.repository.TaskQueueRepository;
import com.linln.admin.system.service.DistributeLockComponent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 初始化任务处理队列线程信息
 */
@Component
@Slf4j
public class TaskQueueThreadInitService implements ApplicationListener<ApplicationEvent> {
    @Autowired
    private TaskQueueRepository taskQueueRepository;
    @Autowired
    private List<AbstractTaskExecutor> executors;
    @Autowired
    private DistributeLockComponent lockComponent;

    private static Thread execTask = null;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (!(event instanceof ContextRefreshedEvent)) {
            return;
        }
        if (execTask == null) {
            execTask = new Thread(() -> {
                while (true) {
                    try {
                        execTaskOneByOne();
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                    try {
                        Thread.sleep(2000);
                    } catch (Exception interruptedException) {
                    }
                }
            });
            execTask.setName("task_exec_Thread");
            execTask.start();
        }
    }

    /**
     * 一个个任务执行，注意集群部署时的并发问题
     */
    private void execTaskOneByOne() {
        TaskQueue tq = new TaskQueue();
        tq.setStatus(AppConstants.TASK_STATUS_NEW);
        Page<TaskQueue> tqs = taskQueueRepository.findAll(Example.of(tq), PageRequest.of(0, 10, Sort.by("id").ascending()));
        if (tqs.hasContent()) {
            for (TaskQueue t : tqs) {
                String lockey = "execTaskOneByOne:" + t.getId();
                if (!lockComponent.tryLock(lockey, 1200)) {
                    continue;//如果锁已经存在，则不处理
                }
                try {
                    //TODO 集群部署下，需不需要保证任务的执行顺序？同时，如果两个或多个任务同时在执行(特别是针对同一个版本)，会不会互相干扰？ 因此，必须按顺序执行
                    for (AbstractTaskExecutor executor : executors) {//查找合适的任务处理器，对任务进行处理。
                        if (executor.support(t)) {
                            if (executor.exec(t)) {
                                t.setStatus(AppConstants.TASK_STATUS_SUCC);
                            } else {
                                t.setStatus(AppConstants.TASK_STATUS_FAIL);
                            }
                            break;//匹配到第一个就可以
                        }
                    }
                } catch (Throwable e) {
                    log.error(e.getMessage(), e);
                    t.setStatus(AppConstants.TASK_STATUS_FAIL);
                    t.setMsg(StringUtils.trimToEmpty(t.getMsg()) + "\n还发生了严重异常：" + e.getMessage());
                } finally {
                    taskQueueRepository.save(t);
                    lockComponent.releaseLock(lockey);
                }
            }
        }
    }
}
